<!doctype html>
<html lang="zh-CN">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.74.1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>编译 go 源码为 android 动态库(so) | caijinglong的博客</title>
    <meta property="og:title" content="编译 go 源码为 android 动态库(so) - caijinglong的博客">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2020-08-14T20:45:00&#43;08:00">
        
        
    <meta property="article:modified_time" content="2020-08-14T20:45:00&#43;08:00">
        
    <meta name="Keywords" content="golang,go语言,flutter,caijinglong,java,android,博客,项目管理,python,软件架构,公众号,小程序">
    <meta name="description" content="编译 go 源码为 android 动态库(so)">
        
    <meta name="author" content="caijinglong">
    <meta property="og:url" content="http://www.kikt.top/posts/go/compile-for-android/">
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/css/normalize.css">
    
        <link rel="stylesheet" href="/css/prism.css">
    
    <link rel="stylesheet" href="/css/style.css">
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    

   
    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https') {
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else {
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>

    
    
</head>

<body>
<header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="http://www.kikt.top">
                        caijinglong的博客
                    </a>
                
                <p class="description">Android/Flutter开发者，对于golang/python/ios/java 均有所了解</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="" href="http://www.kikt.top">首页</a>
                    
                    <a  href="http://www.kikt.top/archives/" title="归档">归档</a>
                    
                    <a  href="http://www.kikt.top/contact/" title="接单">接单</a>
                    
                    <a  href="http://www.kikt.top/about/" title="关于">关于</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">编译 go 源码为 android 动态库(so)</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2020年8月14日
                        </date>
                        
                        <div class="post-meta meta-category">
                            |
                            
                                <a href="http://www.kikt.top/categories/go">go</a>
                            
                                <a href="http://www.kikt.top/categories/android">android</a>
                            
                        </div>
                        
                        
                        <div class="post-meta">
                            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span> 阅读</span></span>
                        </div>
                        
                        <div class="post-content">
                            <p><code>golang</code>跨平台, 性能强, 周边也算丰富</p>
<p>一直有一个想法, 把这东西写的代码编译成各个平台的库, 未来在 <code>flutter</code> 里用</p>
<h2 id="开发环境和工具">开发环境和工具</h2>
<ul>
<li>MacOS(其他的暂时不考虑)</li>
<li>Go
<ul>
<li>Go 1.14.6, 版本太低可能不能编译成 android 的</li>
<li>Goland , 咱现在是正版用户, 用开源项目申请的<code>All products License</code> , 你也可以根据自己的情况选择别的 IDE 或使用文本编辑器(vscode 也不错)</li>
</ul>
</li>
<li>Android
<ul>
<li>Android Studio</li>
<li>Cmake</li>
<li>Android SDK</li>
<li>Android NDK</li>
</ul>
</li>
</ul>
<h2 id="go-部分">Go 部分</h2>
<h3 id="go-源码">go 源码</h3>
<p><code>add_library.go</code></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>

<span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;C&#34;</span>

<span style="color:#75715e">//export add
</span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">x</span>, <span style="color:#a6e22e">y</span> <span style="color:#66d9ef">int</span>) <span style="color:#66d9ef">int</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">x</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">y</span>
}

<span style="color:#75715e">//export remove_int
</span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">remove_int</span>(<span style="color:#a6e22e">x</span>, <span style="color:#a6e22e">y</span> <span style="color:#66d9ef">int</span>) <span style="color:#66d9ef">int</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">x</span> <span style="color:#f92672">-</span> <span style="color:#a6e22e">y</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
}
</code></pre></div><p>这里有几点要注意</p>
<ol>
<li>package 一定要是 main(强制规定)</li>
<li>一定要包含 main 函数(强制规定)</li>
<li>import &ldquo;C&rdquo;, 不能少, 因为要编译出 c(c++)的头文件</li>
<li>每个方法前要加<code>//export 方法名</code>, 这里要注意
<ol>
<li><code>//</code> 和 <code>export</code>间不能有空格</li>
<li>方法名和 go 的方法名必须完全一样</li>
<li>方法名不能是 c 内置的方法名, 比如<code>remove</code>就不行</li>
</ol>
</li>
</ol>
<h3 id="编译">编译</h3>
<p>我在项目里内置了两个脚本, 一个是编译 android 的, 一个是编译 macOS 的, 因为篇幅和主题的原因, macOS 的就不单独拿出来了</p>
<p>看看编译成安卓的脚本吧</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">export ANDROID_NDK_HOME<span style="color:#f92672">=</span>$ANDROID_HOME/ndk/21.0.6113669

export GOARCH<span style="color:#f92672">=</span>arm
export GOOS<span style="color:#f92672">=</span>android
export CGO_ENABLED<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>
export CC<span style="color:#f92672">=</span>$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang
go build -buildmode<span style="color:#f92672">=</span>c-shared -o output/android/armeabi-v7a/libadd.so add_library.go

echo <span style="color:#e6db74">&#34;Build armeabi-v7a success&#34;</span>

export GOARCH<span style="color:#f92672">=</span>arm64
export GOOS<span style="color:#f92672">=</span>android
export CGO_ENABLED<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>
export CC<span style="color:#f92672">=</span>$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang
go build -buildmode<span style="color:#f92672">=</span>c-shared -o output/android/arm64-v8a/libadd.so add_library.go

echo <span style="color:#e6db74">&#34;Build arm64-v8a success&#34;</span>
</code></pre></div><p>简单来说 有几个环境变量要设置一下</p>
<ol>
<li>GOARCH=CPU 类型</li>
<li>GOOS=设备类型</li>
<li>CGO_ENABLED=1</li>
<li>CC=ndk 里的 clang,需要是对应 CPU 版本的, API 呢建议是最低
<ol>
<li>这里单独说明一下, 高版本的 ndk 用的是 clang, 低版本用的是 gcc, 这个根据你情况来, 个人建议直接用 <code>21.0.6113669</code>版本, 这个我测试过, 没有问题, ndk20 我也试过, 也是 ok 的</li>
</ol>
</li>
<li>go build 命令
<ol>
<li><code>-buildmode=c-shared</code> 构建类型, 使用 <code>go help buildmode</code>查看, 安卓一般用<code>c-shared</code>就可以了, 意思就是 c 类型的共享库(动态库)</li>
<li>-o 后面跟输出的位置, 一般建议使用 libxxx.so 的格式, 相对的, 头文件也会被生成在同一目录下, xxx 就是库的名字, 对应到 Java 里加载库的方法就是<code>System.loadLibrary(&quot;xxx&quot;)</code> 而不是<del><code>System.loadLibrary(&quot;libxxx&quot;)</code></del></li>
<li>最后一个就是需要编译的 go 文件了</li>
</ol>
</li>
</ol>
<p>CPU 类型和设备类型可以使用<code>go tool dist list</code>查看</p>
<p>我这里 1.14.6 包含的 android 对应的是:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ go tool dist list|grep android
android/386
android/amd64
android/arm
android/arm64
</code></pre></div><p>arm 和 arm64 对应 v7 和 v8</p>
<p>386 amd64 应该对应的是 x86 和 x86_64, 这个没有实测, 如果有需要的朋友可以自己尝试一下</p>
<p>运行脚本<code>./build_android_on_mac.sh</code>即可完成编译</p>
<h2 id="安卓">安卓</h2>
<h3 id="创建项目">创建项目</h3>
<p>就是简单的在 Android Studio 中 New Project</p>
<h3 id="cmakeliststxt">CMakeLists.txt</h3>
<p>自己创建一个<code>CMakeLists.txt</code>, 注意大小写敏感, 其实这个文件名可以随便写, 但是约定俗成是这样, 建议不要变</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cmake" data-lang="cmake">cmake_minimum_required(<span style="color:#e6db74">VERSION</span> <span style="color:#e6db74">3.10.2</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>project(<span style="color:#e6db74">android_lib</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>set(<span style="color:#e6db74">GO_BUILD_PATH</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/../go/output/android</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>set(<span style="color:#e6db74">SRC</span> <span style="color:#f92672">${</span>GO_BUILD_PATH<span style="color:#f92672">}</span><span style="color:#e6db74">/</span><span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>include_directories(<span style="color:#f92672">${</span>SRC<span style="color:#f92672">}</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>find_library(
        <span style="color:#e6db74">std-lib</span>
        <span style="color:#e6db74">android</span>
)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>message(<span style="color:#e6db74">&#34;CPU ABI: ${CMAKE_ANDROID_ARCH_ABI}&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>if (<span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span> <span style="color:#e6db74">EQUAL</span> <span style="color:#e6db74">&#34;armeabi&#34;</span>) <span style="color:#75715e">## 根据ABI的不同, 复制so文件到对应的文件夹
</span><span style="color:#75715e"></span>    file(<span style="color:#e6db74">COPY</span> <span style="color:#f92672">${</span>GO_BUILD_PATH<span style="color:#f92672">}</span><span style="color:#e6db74">/</span><span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span><span style="color:#e6db74">/libadd.so</span> <span style="color:#e6db74">DESTINATION</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/libs/</span><span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span>) <span style="color:#75715e"># 复制库文件
</span><span style="color:#75715e"></span>else ()<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>    file(<span style="color:#e6db74">COPY</span> <span style="color:#f92672">${</span>SRC<span style="color:#f92672">}</span><span style="color:#e6db74">/libadd.so</span> <span style="color:#e6db74">DESTINATION</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/libs/</span><span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span>) <span style="color:#75715e"># 复制库文件
</span><span style="color:#75715e"></span>endif ()<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>file(<span style="color:#e6db74">COPY</span> <span style="color:#f92672">${</span>SRC<span style="color:#f92672">}</span><span style="color:#e6db74">/libadd.h</span> <span style="color:#e6db74">DESTINATION</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/src/main/jni</span>) <span style="color:#75715e"># 复制头文件
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>file(<span style="color:#e6db74">GLOB</span> <span style="color:#e6db74">JNI_SRC</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/src/main/jni/*.c</span> <span style="color:#f92672">${</span>CMAKE_CURRENT_SOURCE_DIR<span style="color:#f92672">}</span><span style="color:#e6db74">/src/main/jni/*.h</span>) <span style="color:#75715e"># 扫描jni目录下的头文件和库文件, 并设置给JNI_SRC
</span><span style="color:#75715e"></span>add_library(<span style="color:#e6db74">my_jni</span> <span style="color:#e6db74">SHARED</span> <span style="color:#f92672">${</span>JNI_SRC<span style="color:#f92672">}</span>) <span style="color:#75715e"># 添加jni中转库
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>file(<span style="color:#e6db74">GLOB</span> <span style="color:#e6db74">OUTPUT_LIBRARY</span> <span style="color:#e6db74">libs/</span><span style="color:#f92672">${</span>CMAKE_ANDROID_ARCH_ABI<span style="color:#f92672">}</span><span style="color:#e6db74">/*.so</span>) <span style="color:#75715e"># 找到对应cpu的so库文件
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>target_link_libraries(
        <span style="color:#e6db74">my_jni</span>
        <span style="color:#f92672">${</span>std-lib<span style="color:#f92672">}</span> <span style="color:#75715e"># 连接android标准库给jni, 因为golang用到了一些标准库的定义
</span><span style="color:#75715e"></span>        <span style="color:#f92672">${</span>OUTPUT_LIBRARY<span style="color:#f92672">}</span> <span style="color:#75715e"># 连接给jni库
</span><span style="color:#75715e"></span>)<span style="color:#960050;background-color:#1e0010">
</span></code></pre></div><p>我在其中加入了详细的注释, 其中具体的 Cmake 语法没法展开讲</p>
<h3 id="编写代码">编写代码</h3>
<h4 id="c-代码">C 代码</h4>
<p>首先是 c 代码, 我们在 CMake 中定义了一个 <code>my_jni</code>的库, 这个库包含<code>${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni</code>下所有的 c 和 h 文件</p>
<p>然后吧, 在<code>src/main/jni</code>下创建 c 文件, 我这里定义的是 jni.c,别的什么也没关系</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;jni.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;libadd.h&#34;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
<span style="color:#75715e">//
</span><span style="color:#75715e">// Created by jinglong cai on 2020/8/14.
</span><span style="color:#75715e">//
</span><span style="color:#75715e"></span>
JNIEXPORT jint JNICALL
<span style="color:#a6e22e">Java_top_kikt_usegolibrary_JniLibrary_add</span>(JNIEnv <span style="color:#f92672">*</span>env, jclass clazz, jint x, jint y) {
    <span style="color:#66d9ef">return</span> add(x, y);
<span style="color:#75715e">//    return x + y;
</span><span style="color:#75715e"></span>}

JNIEXPORT jint JNICALL
<span style="color:#a6e22e">Java_top_kikt_usegolibrary_JniLibrary_remove</span>(JNIEnv <span style="color:#f92672">*</span>env, jclass clazz, jint x, jint y) {
    <span style="color:#66d9ef">return</span> remove_int(x, y);
}
</code></pre></div><p>嗯, 其实就两个方法, 对应了 go 里定义的两个方法, 这里是 jni 的写法</p>
<h4 id="java-代码">Java 代码</h4>
<p>JniLibrary.class</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#f92672">package</span> top.kikt.usegolibrary<span style="color:#f92672">;</span>

<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JniLibrary</span> <span style="color:#f92672">{</span>

    <span style="color:#66d9ef">static</span> <span style="color:#f92672">{</span>
        System<span style="color:#f92672">.</span><span style="color:#a6e22e">loadLibrary</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;my_jni&#34;</span><span style="color:#f92672">);</span>
    <span style="color:#f92672">}</span>

    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">native</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">add</span><span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> x<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> y<span style="color:#f92672">);</span>

    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">native</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">remove</span><span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> x<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> y<span style="color:#f92672">);</span>

<span style="color:#f92672">}</span>

</code></pre></div><p>这里的两个 native 方法就是指向<code>jni.h</code>里定义的方法的</p>
<p>MainActivity.kt</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">package</span> top.kikt.usegolibrary

<span style="color:#66d9ef">import</span> androidx.appcompat.app.AppCompatActivity
<span style="color:#66d9ef">import</span> android.os.Bundle
<span style="color:#66d9ef">import</span> kotlinx.android.synthetic.main.activity_main.*

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MainActivity</span> : AppCompatActivity() {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">onCreate</span>(savedInstanceState: Bundle?) {
        <span style="color:#66d9ef">super</span>.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        textView.text = <span style="color:#e6db74">&#34;come from jni ${JniLibrary.add(300, 150)}&#34;</span>

        textView2.text = <span style="color:#e6db74">&#34;come from jni ${JniLibrary.remove(300, 150)}&#34;</span>
    }


}
</code></pre></div><p>就是简单的调用了这两个方法</p>
<h3 id="运行">运行</h3>
<p>结果如下</p>
<p><!-- raw HTML omitted --></p>
<h2 id="后记">后记</h2>
<p>项目代码: <a href="https://github.com/CaiJingLong/BuildGoForAndroid">https://github.com/caijinglong/BuildGoForAndroid</a></p>
<p>本篇为系列的第一篇, 不出意外, 后续应该会有对应的 iOS 篇, flutter-dart:ffi , wasm 篇则待定</p>
<p>以上</p>

                        </div>

                        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/posts/flutter/so-about-1/">flutter android So库对齐</a></li>
        
        <li><a href="/posts/android/ffmpeg/compile-with-ndk20/">Android 记录在macOS上使用 NDK20 编译ffmpeg4.2.2的过程</a></li>
        
        <li><a href="/posts/flutter/desktop/go-desktop-engine/flutter-go-desktop-2/">Go Flutter Desktop (二) go 二进制程序打包为 mac app(dmg)</a></li>
        
        <li><a href="/posts/flutter/desktop/go-desktop-engine/flutter-go-desktop-1/">Go Flutter Desktop (一) 初探</a></li>
        
        <li><a href="/posts/flutter/exists/android-as-aar-to-maven/">把flutter项目作为aar添加到已有的Android工程上</a></li>
        
    </ul>
</div>


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="http://www.kikt.top/tags/android">android</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91">交叉编译</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/go">go</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/so">so</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "caijinglong/kikt-blog-comment"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
                </div>
            </div>
            <div id="secondary">
    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="http://www.kikt.top">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/posts/java/crack/crack1/" title="使用javassist,修改jar包方法实现">使用javassist,修改jar包方法实现</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/github/actions/create/" title="Github action 的开发到发布">Github action 的开发到发布</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/jenkins&#43;fastlane/" title="flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英">flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/go/compile-for-android/" title="编译 go 源码为 android 动态库(so)">编译 go 源码为 android 动态库(so)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-2/" title="Flutter FocusNode 焦点那点事-(二)">Flutter FocusNode 焦点那点事-(二)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-1/" title="Flutter FocusNode 焦点那点事-(一)">Flutter FocusNode 焦点那点事-(一)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/server/caddy/cros_for_local_dev/" title="用 Caddy 解决 web 开发中本地跨域的问题">用 Caddy 解决 web 开发中本地跨域的问题</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/route/navigator-helper2/" title="Navigator Helper2">Navigator Helper2</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/grey-app/" title="flutter 怎么实现app整体灰度">flutter 怎么实现app整体灰度</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/plugin/flutter-sdk-import-aar/" title="Flutter 插件开发之引入aar到安卓部分 并使用本地maven">Flutter 插件开发之引入aar到安卓部分 并使用本地maven</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title">分类</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/categories/android/">android(25)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/caddy/">caddy(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/dart/">dart(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/docker/">docker(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/flutter/">flutter(60)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/github/">github(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/go/">go(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/golang/">golang(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/iOS/">iOS(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/ios/">ios(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/java/">java(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/jetbrains/">jetbrains(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/other/">other(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/python/">python(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/server/">server(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/spring/">spring(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/tools/">tools(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%9D%82%E9%A1%B9/">杂项(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%B6%82%E9%B8%A6/">涂鸦(1)</a>
    </li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title">标签</h3>
<div class="tagcloud">
    
    <a href="http://www.kikt.top/tags/10/">10</a>
    
    <a href="http://www.kikt.top/tags/2.3/">2.3</a>
    
    <a href="http://www.kikt.top/tags/Clipboard/">Clipboard</a>
    
    <a href="http://www.kikt.top/tags/Javassist/">Javassist</a>
    
    <a href="http://www.kikt.top/tags/Migrate/">Migrate</a>
    
    <a href="http://www.kikt.top/tags/aar/">aar</a>
    
    <a href="http://www.kikt.top/tags/actions/">actions</a>
    
    <a href="http://www.kikt.top/tags/aidl/">aidl</a>
    
    <a href="http://www.kikt.top/tags/android/">android</a>
    
    <a href="http://www.kikt.top/tags/androidQ/">androidQ</a>
    
    <a href="http://www.kikt.top/tags/androidX/">androidX</a>
    
    <a href="http://www.kikt.top/tags/apk/">apk</a>
    
    <a href="http://www.kikt.top/tags/app/">app</a>
    
    <a href="http://www.kikt.top/tags/appcode/">appcode</a>
    
    <a href="http://www.kikt.top/tags/azure/">azure</a>
    
    <a href="http://www.kikt.top/tags/ble/">ble</a>
    
    <a href="http://www.kikt.top/tags/bottomsheet/">bottomsheet</a>
    
    <a href="http://www.kikt.top/tags/butterknife/">butterknife</a>
    
    <a href="http://www.kikt.top/tags/c/">c</a>
    
    <a href="http://www.kikt.top/tags/caddy/">caddy</a>
    
    <a href="http://www.kikt.top/tags/camera/">camera</a>
    
    <a href="http://www.kikt.top/tags/cameraX/">cameraX</a>
    
    <a href="http://www.kikt.top/tags/channel/">channel</a>
    
    <a href="http://www.kikt.top/tags/cocoapods/">cocoapods</a>
    
    <a href="http://www.kikt.top/tags/cupertino/">cupertino</a>
    
    <a href="http://www.kikt.top/tags/dart/">dart</a>
    
    <a href="http://www.kikt.top/tags/dart-2.6/">dart-2.6</a>
    
    <a href="http://www.kikt.top/tags/desktop/">desktop</a>
    
    <a href="http://www.kikt.top/tags/dialog/">dialog</a>
    
    <a href="http://www.kikt.top/tags/dmg/">dmg</a>
    
    <a href="http://www.kikt.top/tags/doc/">doc</a>
    
    <a href="http://www.kikt.top/tags/docker/">docker</a>
    
    <a href="http://www.kikt.top/tags/docker-compose/">docker-compose</a>
    
    <a href="http://www.kikt.top/tags/excel/">excel</a>
    
    <a href="http://www.kikt.top/tags/exists/">exists</a>
    
    <a href="http://www.kikt.top/tags/faq/">faq</a>
    
    <a href="http://www.kikt.top/tags/fastlane/">fastlane</a>
    
    <a href="http://www.kikt.top/tags/ffi/">ffi</a>
    
    <a href="http://www.kikt.top/tags/ffmpeg/">ffmpeg</a>
    
    <a href="http://www.kikt.top/tags/flexmark/">flexmark</a>
    
    <a href="http://www.kikt.top/tags/flutter/">flutter</a>
    
    <a href="http://www.kikt.top/tags/flutter-web/">flutter-web</a>
    
    <a href="http://www.kikt.top/tags/focus/">focus</a>
    
    <a href="http://www.kikt.top/tags/focusNode/">focusNode</a>
    
    <a href="http://www.kikt.top/tags/form/">form</a>
    
    <a href="http://www.kikt.top/tags/framework/">framework</a>
    
    <a href="http://www.kikt.top/tags/github/">github</a>
    
    <a href="http://www.kikt.top/tags/go/">go</a>
    
    <a href="http://www.kikt.top/tags/golang/">golang</a>
    
    <a href="http://www.kikt.top/tags/gradle/">gradle</a>
    
    <a href="http://www.kikt.top/tags/http/">http</a>
    
    <a href="http://www.kikt.top/tags/iOS/">iOS</a>
    
    <a href="http://www.kikt.top/tags/ide/">ide</a>
    
    <a href="http://www.kikt.top/tags/idea/">idea</a>
    
    <a href="http://www.kikt.top/tags/index/">index</a>
    
    <a href="http://www.kikt.top/tags/inside/">inside</a>
    
    <a href="http://www.kikt.top/tags/ios/">ios</a>
    
    <a href="http://www.kikt.top/tags/java/">java</a>
    
    <a href="http://www.kikt.top/tags/jenkins/">jenkins</a>
    
    <a href="http://www.kikt.top/tags/jetbrains/">jetbrains</a>
    
    <a href="http://www.kikt.top/tags/jsdelivr/">jsdelivr</a>
    
    <a href="http://www.kikt.top/tags/json/">json</a>
    
    <a href="http://www.kikt.top/tags/json_serializable/">json_serializable</a>
    
    <a href="http://www.kikt.top/tags/junit/">junit</a>
    
    <a href="http://www.kikt.top/tags/library/">library</a>
    
    <a href="http://www.kikt.top/tags/live-template/">live-template</a>
    
    <a href="http://www.kikt.top/tags/loadmore/">loadmore</a>
    
    <a href="http://www.kikt.top/tags/log/">log</a>
    
    <a href="http://www.kikt.top/tags/mac/">mac</a>
    
    <a href="http://www.kikt.top/tags/macos/">macos</a>
    
    <a href="http://www.kikt.top/tags/maven/">maven</a>
    
    <a href="http://www.kikt.top/tags/mysql/">mysql</a>
    
    <a href="http://www.kikt.top/tags/navigationbar/">navigationbar</a>
    
    <a href="http://www.kikt.top/tags/navigator/">navigator</a>
    
    <a href="http://www.kikt.top/tags/ndk/">ndk</a>
    
    <a href="http://www.kikt.top/tags/nginx/">nginx</a>
    
    <a href="http://www.kikt.top/tags/oc/">oc</a>
    
    <a href="http://www.kikt.top/tags/opencv/">opencv</a>
    
    <a href="http://www.kikt.top/tags/poi/">poi</a>
    
    <a href="http://www.kikt.top/tags/pub/">pub</a>
    
    <a href="http://www.kikt.top/tags/python/">python</a>
    
    <a href="http://www.kikt.top/tags/retrofit/">retrofit</a>
    
    <a href="http://www.kikt.top/tags/route/">route</a>
    
    <a href="http://www.kikt.top/tags/server/">server</a>
    
    <a href="http://www.kikt.top/tags/so/">so</a>
    
    <a href="http://www.kikt.top/tags/spp/">spp</a>
    
    <a href="http://www.kikt.top/tags/sprintboot/">sprintboot</a>
    
    <a href="http://www.kikt.top/tags/swift/">swift</a>
    
    <a href="http://www.kikt.top/tags/tap/">tap</a>
    
    <a href="http://www.kikt.top/tags/thymeleaf/">thymeleaf</a>
    
    <a href="http://www.kikt.top/tags/tips/">tips</a>
    
    <a href="http://www.kikt.top/tags/toast/">toast</a>
    
    <a href="http://www.kikt.top/tags/transition/">transition</a>
    
    <a href="http://www.kikt.top/tags/ui/">ui</a>
    
    <a href="http://www.kikt.top/tags/utf8/">utf8</a>
    
    <a href="http://www.kikt.top/tags/yield/">yield</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/">二进制</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89/">交叉</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/">交叉编译</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%8B%E7%BB%8D/">介绍</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/">代码规范</a>
    
    <a href="http://www.kikt.top/tags/%E5%86%B2%E7%AA%81/">冲突</a>
    
    <a href="http://www.kikt.top/tags/%E5%8A%A8%E6%80%81/">动态</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%91%E5%B8%83/">发布</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%AF%E6%89%A7%E8%A1%8C/">可执行</a>
    
    <a href="http://www.kikt.top/tags/%E5%9B%BE%E5%BA%8A/">图床</a>
    
    <a href="http://www.kikt.top/tags/%E5%9F%8E%E5%B8%82/">城市</a>
    
    <a href="http://www.kikt.top/tags/%E5%A4%A7%E6%96%87%E4%BB%B6/">大文件</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%A5%E5%85%B7/">工具</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE/">已有项目</a>
    
    <a href="http://www.kikt.top/tags/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/">开发环境</a>
    
    <a href="http://www.kikt.top/tags/%E6%80%BB%E7%BB%93/">总结</a>
    
    <a href="http://www.kikt.top/tags/%E6%85%A2/">慢</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%93%E5%8C%85/">打包</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%AB%E7%A0%81/">扫码</a>
    
    <a href="http://www.kikt.top/tags/%E6%8F%92%E4%BB%B6/">插件</a>
    
    <a href="http://www.kikt.top/tags/%E6%96%87%E6%A1%A3/">文档</a>
    
    <a href="http://www.kikt.top/tags/%E6%A8%A1%E6%9D%BF/">模板</a>
    
    <a href="http://www.kikt.top/tags/%E6%B8%B2%E6%9F%93/">渲染</a>
    
    <a href="http://www.kikt.top/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">源码解析</a>
    
    <a href="http://www.kikt.top/tags/%E7%81%B0%E5%BA%A6/">灰度</a>
    
    <a href="http://www.kikt.top/tags/%E7%82%B9%E5%87%BB/">点击</a>
    
    <a href="http://www.kikt.top/tags/%E7%84%A6%E7%82%B9/">焦点</a>
    
    <a href="http://www.kikt.top/tags/%E7%88%AC%E8%99%AB/">爬虫</a>
    
    <a href="http://www.kikt.top/tags/%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/">状态管理</a>
    
    <a href="http://www.kikt.top/tags/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/">百度地图</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A0%81/">编码</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A8%8B%E6%8A%80%E5%B7%A7/">编程技巧</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E8%AF%91/">编译</a>
    
    <a href="http://www.kikt.top/tags/%E7%BF%BB%E8%AF%91/">翻译</a>
    
    <a href="http://www.kikt.top/tags/%E8%93%9D%E7%89%99/">蓝牙</a>
    
    <a href="http://www.kikt.top/tags/%E8%A1%A8%E5%8D%95/">表单</a>
    
    <a href="http://www.kikt.top/tags/%E8%B7%AF%E7%94%B1/">路由</a>
    
    <a href="http://www.kikt.top/tags/%E8%BF%87%E5%9C%BA%E5%8A%A8%E7%94%BB/">过场动画</a>
    
    <a href="http://www.kikt.top/tags/%E9%9A%90%E7%A7%81/">隐私</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="https://github.com/Caijinglong" title="我的Github">我的Github</a>
        </li>
        
        <li>
            <a target="_blank" href="https://flutter.dev" title="flutter 官网">flutter 官网</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="http://www.kikt.top/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        <div>
            <p>如果我的blog对您有帮助,而您愿意捐赠 😆, 可以用下面的二维码</p>
            <img style="width:5rem;" src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141834.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141931.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424142013.png"/>
        </div>
        &copy; 2018 <a href="http://www.kikt.top">caijinglong的博客 By caijinglong</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="http://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/rujews/maupassant-hugo" target="_blank">maupassant</a>.<br/>
        本站所有内容基于<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" style="color:coral">CC4.0</a>协议发布,需要转载必须署名
        <br/>
        如果有问题可以<a href="mailto:cjl_spy@163.com">email联系我</a>
        <br/>
        京ICP备18038473号-1
        
    </div>
</footer>


    <script type="text/javascript" src="/js/prism.js" async="true"></script>
    <script type="text/javascript">
    
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())

    window.MathJax = {
        tex2jax: {
            inlineMath: [ ['$','$'] ],
            processEscapes: true
        }
    };
    </script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/js/totop.js?v=0.0.0" async=""></script>



<script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




</body>
</html>
